use std::{pin::Pin, sync::LazyLock};

use axum::{
  body::Body,
  http::{Request, Response, header},
};
use tower_http::auth::{AsyncAuthorizeRequest, AsyncRequireAuthorizationLayer};

static AUTH_LAYER: LazyLock<AsyncRequireAuthorizationLayer<JWTAuth>> =
  LazyLock::new(|| {
    AsyncRequireAuthorizationLayer::new(JWTAuth::new(get_jwt()))
  });

use super::{
  auth::{JWT, get_jwt},
  error::ApiError,
};

#[derive(Clone)]
pub struct JWTAuth {
  jwt: &'static JWT,
}

impl JWTAuth {
  pub fn new(jwt: &'static JWT) -> Self {
    Self { jwt }
  }
}

impl AsyncAuthorizeRequest<Body> for JWTAuth {
  type RequestBody = Body;

  type ResponseBody = Body;

  type Future = Pin<
    Box<
      dyn Future<
          Output = Result<
            Request<Self::RequestBody>,
            Response<Self::ResponseBody>,
          >,
        > + Send
        + 'static,
    >,
  >;

  fn authorize(&mut self, mut request: Request<Body>) -> Self::Future {
    let jwt = self.jwt;

    Box::pin(async move {
      let token = request
        .headers()
        .get(header::AUTHORIZATION)
        .map(|value| -> Result<_, ApiError> {
          let tk = value
            .to_str()
            .map_err(|_| {
              ApiError::Unauthenticated(
                "Authorization请求头不是一个有效的字符串".to_string(),
              )
            })?
            .strip_prefix("Bearer ")
            .ok_or_else(|| {
              ApiError::Unauthenticated(
                "Authorization请求头必须以 Bearer 开头".to_string(),
              )
            })?;
          Ok(tk)
        })
        .transpose()? // 把option<Result<xx, xx>>转成 option<xx>
        .ok_or_else(|| {
          ApiError::Unauthenticated("Authorization请求头必须存在".to_string())
        })?;

      let principal =
        jwt.decode(token).map_err(|err| ApiError::Internal(err))?;
      request.extensions_mut().insert(principal);
      Ok(request)
    })
  }
}

pub fn get_auth_layer() -> &'static AsyncRequireAuthorizationLayer<JWTAuth> {
  &AUTH_LAYER
}
